<?php

defined('YF_PATH') or exit();

/**
  +------------------------------------------------------------------------------
 *  数据库中间层实现类
 * 2013-09-15 Mysql数据驱动 并使用PDO
  +------------------------------------------------------------------------------

  +------------------------------------------------------------------------------
 */
class Db {

    // 数据库类型
    protected $dbType = null;
    // 是否自动释放查询结果
    protected $autoFree = false;
    // 是否显示调试信息 如果启用会在日志文件记录sql语句
    public $debug = false;
    // 是否使用永久连接
    protected $pconnect = false;
    // 当前SQL指令
    protected $queryStr = '';
    // 最后插入ID
    protected $lastInsID = null;
    // 返回或者影响记录数
    protected $numRows = 0;
    // 错误信息
    protected $error = '';
    // 当前查询ID
    protected $queryID = null;
    // 是否已经连接数据库
    protected $connected = false;
    // 数据库连接参数配置
    protected $config = '';
    // SQL 执行时间记录
    protected $beginTime;
    //使用的数据库配置名称
    protected $dbConfigName = 'DEFAULT';
    protected $db = null;
    protected $trueTableName = '';

    /**
      +----------------------------------------------------------
     * 架构函数
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param array $config 数据库配置数组
     * @param str $database 数据库配置名称
      +----------------------------------------------------------
     */
    function __construct($config = '', $dbConfigName = '') {
        if (!empty($dbConfigName)) {
            $this->dbConfigName = $dbConfigName;
        }
        return $this->factory($config);
    }

    /**
      +----------------------------------------------------------
     * 取得数据库类实例
      +----------------------------------------------------------
     * @static
     * @access public
     * @param array $config 数据库配置数组
     * @param str $dbConfigName 数据库配置名称
      +----------------------------------------------------------
     * @return mixed 返回数据库驱动类
      +----------------------------------------------------------
     */
    public static function getInstance($config = array(), $dbConfigName = '') {
        $dbObj = new Db($config, $dbConfigName);
        $dbObj->db = $dbObj->factory($config);
        return $dbObj;
    }

    /**
      +----------------------------------------------------------
     * 加载数据库 支持配置文件或者 
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @param mixed $db_config 数据库配置信息
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     * @throws ThinkExecption
      +----------------------------------------------------------
     */
    public function factory($db_config = '') {
        // 读取数据库配置
        $db_config = $this->parseConfig($db_config);
        try {
            $params = array(PDO::ATTR_PERSISTENT => true, PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES '" . $db_config['DB_CHARSET'] . "'", PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
            $db = new PDO('mysql:host=' . $db_config['DB_HOST'] . ';dbname=' . $db_config['DB_NAME'], $db_config['DB_USER'], $db_config['DB_PWD'], $params);
        } catch (PDOException $e) {
            throwException($e, '', $e->getCode());
        }
        return $db;
    }

    /**
      +----------------------------------------------------------
     * 分析数据库配置信息，支持数组
      +----------------------------------------------------------
     * @access private
      +----------------------------------------------------------
     * @param mixed $db_config 数据库配置信息
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    private function parseConfig($db_config = '') {
        if (empty($db_config)) {
            // 如果配置为空，读取配置文件设置
            $dbAllConfig = getConfig('DATABASE');
            $db_config = $dbAllConfig[$this->dbConfigName];
//            $db_config = array (
//                'dbms'        => getConfig('DB_TYPE'),
//                'username'  =>   getConfig('DB_USER'),
//                'password'   =>   getConfig('DB_PWD'),
//                'host'  =>   getConfig('DB_HOST'),
//                'port'    =>   getConfig('DB_PORT'),
//                'database'   =>   getConfig('DB_NAME'),
//                'dsn'          =>   getConfig('DB_DSN'),
//                'params'     =>   getConfig('DB_PARAMS'),
//            );
        }
        return $db_config;
    }

    /**
     * SQL 执行
     */
    function query($sql) {
        return $this->db->query($sql);
    }

    /**
     * 基础查询函数
     * @param array $whereDate 条件的参数值 $key 必须和$whereSql相同顺序的:user相同
     * @param str $whereSql 条件的sql字符串,格式 user = :user
     * @param str $limit 分页
     * @param str $orderby 排序方式
     * @param str $data 返回的字段
     * @param str $groupby 分组方式
     */
    function select($whereDate = array(), $whereSql = '1=1', $orderby = '', $data = '*', $groupby = '', $page = '', $pageSize = '') {
        $orderby = $orderby ? ' ORDER BY ' . $orderby : '';
        $groupby = $groupby ? ' GROUP BY ' . $groupby : '';
        if (intval($page) && intval($pageSize)) {
            $this->total = $this->count($whereDate, $whereSql, $groupby);
            $this->pages = pages($this->total, $page, $pageSize);
            $page = max(intval($page), 1);
            $offset = $pageSize * ($page - 1);
            $limit = "LIMIT $offset,$pageSize";
        }
        $sql = 'SELECT ' . $data . ' FROM ' . $this->getTableName() . ' WHERE ' . $whereSql . ' ' . $groupby . ' ' . $orderby . ' ' . $limit;
        try {
            $stmt = $this->db->prepare($sql);
            $stmt->execute($whereDate); //参数绑定 
            $stmt->setFetchMode(PDO::FETCH_ASSOC); //关联数组形式（只通过字段名下标访问数组内容）
        } catch (PDOException $e) {
            throwException($e, '', $e->getCode());
        }
        $queryData = $stmt->fetchAll();
        ;
        return $queryData;
    }

    /**
     * 基础查询函数
     * @param array $whereDate 条件的参数值 $key 必须和$whereSql相同顺序的:user相同
     * @param str $whereSql 条件的sql字符串,格式 user = :user
     * @param str $orderby 排序方式
     * @param int $page 页码
     * @param int $pageSize 每页显示条数
     * @param str $data 返回的字段
     * @param str $groupby 分组方式
     */
    function listInfo($whereDate = array(), $whereSql = '1=1', $page = 1, $pageSize = 20, $orderby = '', $data = '*', $groupby = '') {
        $this->total = $this->count($whereDate, $whereSql, $groupby);
        $this->pages = pages($this->total, $page, $pageSize);
        $page = max(intval($page), 1);
        $offset = $pageSize * ($page - 1);
        $limit = "$offset,$pageSize";
        if ($this->total > 0) {
            return $this->select($whereDate, $whereSql, $limit, $orderby, $data, $groupby);
        } else {
            return array();
        }
    }

    /**
     * 取得单条查询记录
     * @param array $whereDate 条件的参数值 $key 必须和$whereSql相同顺序的:user相同
     * @param str $whereSql 条件的sql字符串,格式 user = :user
     * @param str $orderby 排序方式
     * @param str $data 返回的字段
     * @param str $groupby 分组方式
     */
    function getOne($whereDate = array(), $whereSql = '1=1', $data = '*', $orderby = '', $groupby = '') {
        $orderby = $orderby ? ' ORDER BY ' . $orderby : '';
        $groupby = $groupby ? ' GROUP BY ' . $groupby : '';
        $sql = 'SELECT ' . $data . ' FROM ' . $this->getTableName() . ' WHERE ' . $whereSql . ' ' . $groupby . ' ' . $orderby . '  LIMIT 0,1';
        try {
            $stmt = $this->db->prepare($sql);
            $stmt->execute($whereDate); //参数绑定 
            $stmt->setFetchMode(PDO::FETCH_ASSOC); //关联数组形式（只通过字段名下标访问数组内容）
        } catch (PDOException $e) {
            throwException($e, '', $e->getCode());
        }
        $queryData = array();
        foreach ($stmt as $row) {
            $queryData = $row;
            break;
        }
        return $queryData;
    }

    /**
     * 取得单条查询记录
     * @param array $whereDate 条件的参数值 $key 必须和$whereSql相同顺序的:user相同
     * @param str $whereSql 条件的sql字符串,格式 user = :user
     * @param str $orderby 排序方式
     * @param str $data 返回的字段
     * @param str $groupby 分组方式
     */
    function getValue($whereDate = array(), $whereSql = '1=1', $data = '*', $orderby = '', $groupby = '') {
        $queryData = $this->getOne($whereDate, $whereSql, $data, $orderby, $groupby);
        return array_shift($queryData);
    }

    /**
     * 符合的记录总数
     */
    function count($whereDate = array(), $whereSql = '1=1', $groupby = '') {
        return $this->getValue($whereDate, $whereSql, 'COUNT(1) AS num');
    }

    /**
     * 保存数据
     * @param array $data 保存的数据,key为字段,value为值
     */
    function insert($data) {
        $fields = '';
        $values = '';
        $val = array();
        foreach ($data as $key => $v) {
            if ($key) {
                if ($fields) {
                    $fields .= ',`' . $key . '`';
                    $values .= ',?';
                } else {
                    $fields .= '`' . $key . '`';
                    $values .= '?';
                }
                $val[] = $v;
            }
        }
        $sql = 'INSERT INTO ' . $this->getTableName() . '(' . $fields . ') VALUES(' . $values . ')';
        try {
            $stmt = $this->db->prepare($sql);
            return $stmt->execute($val);
        } catch (PDOException $e) {
            throwException($e, '', $e->getCode());
        }
    }

    /**
     * 更新数据
     * @param array $data 保存的数据,key为字段,value为值
     * @param array $whereDate 条件的参数值 $key 必须和$whereSql相同顺序的:user相同
     * @param str $whereSql 条件的sql字符串,格式 user = :user
     * @return int 影响的行数
     */
    function update($data, $whereDate = array(), $whereSql = '') {
        $whereSql = $whereSql ? ' WHERE ' . $whereSql : '';
        $dataFields = '';
        $val = array();
        foreach ($data as $key => $v) {
            if ($key) {
                if ($dataFields) {
                    $dataFields .= ',`' . $key . '` = :u_' . $key;
                } else {
                    $dataFields .= '`' . $key . '` = :u_' . $key;
                }
                $val[':u_' . $key] = $v;
            }
        }
        $val = array_merge($val, $whereDate);

        $sql = 'UPDATE ' . $this->getTableName() . ' SET ' . $dataFields . ' ' . $whereSql;
        try {
            $stmt = $this->db->prepare($sql);
            $stmt->execute($val);
            return $stmt->rowCount();
        } catch (PDOException $e) {
            throwException($e, '', $e->getCode());
        }
    }

    /**
     * 删除
     * @param array $whereDate 条件的参数值 $key 必须和$whereSql相同顺序的:user相同
     * @param str $whereSql 条件的sql字符串,格式 user = :user
     * @return int 影响的行数
     */
    function delete($whereDate = array(), $whereSql = '') {
        $whereSql = $whereSql ? ' WHERE ' . $whereSql : '';
        $sql = ' DELETE ' . $this->getTableName() . '  ' . $whereSql;
        try {
            $stmt = $this->db->prepare($sql);
            $stmt->execute($whereDate);
            return $stmt->rowCount();
        } catch (PDOException $e) {
            throwException($e, '', $e->getCode());
        }
    }

    /**
     * 取得插入的ID
     */
    function insertId() {
        return $this->db->lastInsertId();
    }

    /**
     * 获取表字段
     * @param $table 		数据表
     * @return array
     */
    public function get_fields($table) {
        $table = $table ? $table : $this->getTableName();
        $fields = array();
        $this->execute("SHOW COLUMNS FROM $table");
        while ($r = $this->fetch_next()) {
            $fields[$r['Field']] = $r['Type'];
        }
        return $fields;
    }

    /**
     * 开启事务
     */
    function beginTransaction() {
        $this->db->beginTransaction();
    }

    /**
     * 提交事务
     */
    function commit() {
        $this->db->commit();
    }

    /**
     * 回滚事务
     */
    function rollBack() {
        $this->db->rollBack();
    }

    /**
     * 设置操作的表,不包含前缀
     */
    function getTableName() {
        if ($this->trueTableName) {
            return $this->trueTableName;
        } else {
            throwException('tanle name not exist');
        }
    }

    /**
     * 设置操作的表,不包含前缀
     */
    function setTableName($tableName) {
        $this->trueTableName = $this->tablePrefix . $tableName;
    }

    /**
      +----------------------------------------------------------
     * 数据库调试 记录当前SQL
      +----------------------------------------------------------
     * @access protected
      +----------------------------------------------------------
     */
    protected function debug() {
        // 记录操作结束时间
        if ($this->debug) {
            $runtime = number_format(microtime(TRUE) - $this->beginTime, 6);
            Log::record(" RunTime:" . $runtime . "s SQL = " . $this->queryStr, Log::SQL);
        }
    }

    /**
      +----------------------------------------------------------
     * 获取最近的错误信息
      +----------------------------------------------------------
     * @access public
      +----------------------------------------------------------
     * @return string
      +----------------------------------------------------------
     */
    public function getError() {
        return $this->error;
    }

    /**
     * 析构函数,对象销毁的时候调用
     */
    function __destruct() {
        if ($this->db) {
            $this->db = null;
        }
    }

}

//类定义结束
?>